<?php
/**
* Node Class for Shuffler_Model_Tree
* 
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* 
* @copyright Copyright (c) 2008-2009 Ne8, LLC <josh.ribakoff@gmail.com>
* @license New BSD License
* @version 0.1
* @category Shuffler
* @package Shuffler_Model                     
* @subpackage Tree
*/
class Shuffler_Model_Tree_Node extends Shuffler_Model implements Shuffler_Model_Tree_Node_Interface
{
    /**
    * @var Shuffler_Model_Tree_Node_Interface parent node
    */
    protected $parent = null;

    /**
    * @Var Shuffler_Collection of Shuffler_Model_Tree_Node_Interface   all children for this node, only direct descendants
    */
    protected $children = NULL;
    
    /**
    * @var Shuffler_Collection of Shuffler_Model_Tree_Node_Interface  this node's descendants
    */
    protected $descendants;
    
    /**
    * @var string title of this node
    */
    protected $title;
    
    /**
    * @var Shuffler_Model_Tree_interface
    */
    protected $tree;    
    
    protected $level; 
 
    /**
    * Takes a name as constructor
    * 
    * @param int id
    * @param string name
    */
    public function __construct( $id = 0, $title = '' )
    {
        parent::__construct( $id );
        $this->title = $title;  
    }      
    
    /**
    * Add a child node
    * 
    * @param an instance of Shuffler_Model_Tree_Node_Interface
    */
    public function addChild( Shuffler_Model_Tree_Node_Interface $node )
    {
        $node->setParent( $this );
        $this->getChildren()->addModel( $node );
    }
    
    /**
    * @return Shuffler_Model_Tree_Interface
    */
    public function getTree()
    {
        $tree = new Shuffler_Model_Tree();
        $tree->setRoot( $this );
        return $tree;
    }
    
    /**
    * Get depth of node in tree, root node is at depth 0
    *
    * @return int depth
    */
    public function getDepth()
    {
        // If the parent is not loaded yet rever to SQL lookup because recursion would result in circular DBmapper references
        if( $this->parent instanceof Shuffler_Null )
        {
           return Shuffler_Factory::getMapper( get_class( $this ) )->getDepth( $this );
        }
        if( is_null( $this->parent ) )
        {
            return 0;
        }
        if( !is_object( $this->getParent() ) )
        {
            throw new Exception();
        }
        return $this->getParent()->getDepth() + 1;
    }
    
    /**
    * Set the title of this node
    * 
    * @param string title to set to
    */
    public function setTitle( $title )
    {
        $this->title = $title;
        return $this;
    }
    
    /**
    * Set this node's parent to a node
    * 
    * @param Shuffler_Model_Tree_Node_Interface
    */
    public function setParent( Shuffler_Model_Tree_Node_Interface $parent )
    {
        if( $this->getDescendants()->contains( $parent ) )
        {
            foreach( $this->getChildren() as $child )
            {
                $child->setParent( $this->getParent() );
            }
        }
        $this->parent = $parent;
        return $this;
    }
    
    /**
    * Get this node's parent node
    * 
    * @return an instance of Shuffler_Model_Tree_Node_Interface
    * @todo add tests for returning NULL models
    */
    public function getParent()
    {
        if( $this->parent instanceof Shuffler_Null )
        {
            return Shuffler_Factory::createNullModel( get_class( $this ) );
        }
        if( is_null( $this->parent ) ) 
        {
            return Shuffler_Factory::createNullModel( get_class( $this ) );   
            //$this->parent = $this->getMapper()->findParent( $this );
        }
        if( is_null( $this->parent ) )
        {
            if( $this instanceof Shuffler_Null )
            {
                return Shuffler_Factory::createNullModel( get_class( $this ) );
            }
        }
        return $this->parent;
    }
    
    public function getParentId()
    {
        if( is_null( $this->parent ) )
        {
            return 0;
        }
        return $this->parent->getId();
    }
    
    /**
    * Get the title of this node
    * 
    * @return string title
    */
    public function getTitle()
    {
        return $this->title;
    }
    
    /**
    * Get direct descendants
    * 
    * If you want all descendants, use the getDescendants() method. 
    * 
    * @return array of children
    */
    public function getChildren()   
    {
        if( !is_null( $this->children ) )
        {
            return $this->children;
        }
        $this->children = new Shuffler_Collection( array() );
        return $this->children;   
    }
    
    public function setChildren( Shuffler_Collection $collection )
    {
        $this->children = $collection;
    }
    
    /**
    * Get all descendants
    * 
    * @return array of descendants
    */
    public function getDescendants()   
    {
        if( !is_null( $this->descendants ) )
        {
            return new Shuffler_Collection( $this->descendants );
        }
        $return = new Shuffler_Collection();
        $children = $this->getChildren();
        foreach( $children as $child )
        {
            $return->addModel( $child );
            $return->merge( $child->getDescendants() );    
        }
        return $return; 
    }
    
    /**
    * Check if this node has child nodes
    * 
    * @return boolean
    */
    public function hasChildren(  )   
    {
        return $this->getChildren()->count() > 0;   
    }
    
    public function getPath()
    {
        $path = array();
        $node = $this;
        for( $level = $this->getLevel(); $level >= 1; $level-- )
        {
            array_unshift( $path, $node );
            $node = $node->getParent();
        }
        return $path;
    }
    
    public function getLevel()
    {
        return $this->level;
    }   
}